package com.dwxt.community.service;


import com.dwxt.common.base.*;
import com.dwxt.common.util.PartyUtil;
import com.dwxt.community.dao.*;
import com.dwxt.community.entity.*;
import com.dwxt.community.entity.dto.*;
import com.github.tobato.fastdfs.domain.fdfs.*;
import com.github.tobato.fastdfs.service.*;
import lombok.extern.slf4j.*;
import net.coobird.thumbnailator.Thumbnails;
import org.apache.commons.io.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.codec.binary.Base64;
import org.slf4j.*;
import org.springframework.beans.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.beans.factory.support.*;
import org.springframework.stereotype.*;
import org.springframework.transaction.annotation.*;
import org.springframework.web.multipart.*;

import java.io.*;
import java.text.*;
import java.util.*;

@Service
public class FileService {
    private Logger log = LoggerFactory.getLogger(FileService.class);

    @Value("${gate.file.uploadPath}")
    private String uploadPath;
    @Value("${gate.file.chunkPath}")
    private String chunkPath;
    @Value("${gate.file.mergePath}")
    private String mergePath;

    @Value("${gate.file.realPath}")
    private String realPath;

    @Value("${gate.file.server}")
    private String server;
    @Value("${fdfs.server}")
    private String fdfsServer;
    @Autowired
    private AppendFileStorageClient appendFileStorageClient;
    @Autowired
    private FastFileStorageClient fastFileStorageClient;
    @Autowired
    private ChunkInfoMapper chunkInfoMapper;

    /**
     * 保存到 FDFS
     *
     * @param file
     * @return
     * @throws IOException
     */
    public String saveFile(MultipartFile file) throws IOException {
        StorePath storePath = fastFileStorageClient.uploadFile(
                file.getInputStream(), file.getSize(),
                file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1),
                new ManagedSet<>());
        return fdfsServer + "/" + storePath.getFullPath();
    }

    /**
     * 保存到本地
     *
     * @param file
     * @param fileName
     * @return
     * @throws IOException
     */
    public String saveFile(MultipartFile file, String fileName) throws IOException {

        log.info("保存文件开始");
        /**
         * 首先将文件缓存到服务器上
         */
        fileName = createFileName(file.getOriginalFilename(), fileName);
        String userId = PartyUtil.getCurrentUserId();
        File dir = new File(realPath + uploadPath + "/" + userId);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        String savePath = realPath + uploadPath + "/" + userId + "/" + fileName;
        File saveFile = new File(savePath);
        file.transferTo(saveFile);
        // 先尝试压缩并保存图片
        //Thumbnails.of(file.getInputStream()).scale(1f).outputQuality(0.25f).toFile(saveFile);
        // 设置访问权限，可读，所有人可读
        saveFile.setReadable(true, false);
        log.info("保存文件结束");
        return server + uploadPath + "/" + userId + "/" + fileName;
    }

    /**
     * @方法名：
     * @描述：
     * @作者： lijing
     * @日期： 2018年9月20日
     */
    public String savefile(String base64File) throws IOException {
        log.info("保存文件开始");
        String suffix = "";
        if (base64File.startsWith("data:image/png")) {
            suffix = "png";
            base64File = base64File.replaceAll("data:image/png;base64,", "");
        } else if (base64File.startsWith("data:image/jpeg")) {
            suffix = "jpg";
            base64File = base64File.replaceAll("data:image/jpeg;base64,", "");
        } else if (base64File.startsWith("data:image/jepg")) {
            suffix = "jpg";
            base64File = base64File.replaceAll("data:image/jepg;base64,", "");
        } else if (base64File.startsWith("data:image/jpg")) {
            suffix = "jpg";
            base64File = base64File.replaceAll("data:image/jpg;base64,", "");
        } else if (base64File.startsWith("data:audio/mp3")) {
            suffix = "mp3";
            base64File = base64File.replaceAll("data:audio/mp3;base64,", "");
        }

        byte[] buffer = Base64.decodeBase64(base64File);
        String fileName = createBase64FileName(suffix);
        File dir = new File(realPath + uploadPath);
        if (!dir.exists()) {
            dir.mkdir();
        }
        String savePath = realPath + uploadPath + "/" + fileName;
        FileOutputStream out = new FileOutputStream(savePath);
        out.write(buffer);
        out.close();
        return savePath;
    }


    private String createFileName(String originalFilename, String fileName) {
        String result = "";
        // 获取文件后缀
        String suffix = getFileSuffix(originalFilename);
        if (StringUtils.isNotBlank(fileName)) {
            result = fileName + "." + suffix;
        } else {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
            String date = sdf.format(new Date());
            if (StringUtils.isNotBlank(suffix)) {
                result = date + "." + suffix;
            }
        }
        return result;
    }

    private String createBase64FileName(String suffix) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        String date = sdf.format(new Date());
        return date + "." + suffix;
    }

    private String getFileSuffix(String fileName) {
        // 获取文件后缀
        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
        if (StringUtils.isNotBlank(suffix)) {
            suffix = suffix.toLowerCase();
        }
        return suffix;
    }

    /**
     * 分片上传
     *
     * @param chunkUploadInfo
     * @return
     */
    public BaseResult chunkUpload(ChunkUploadInfo chunkUploadInfo) {
        //用户上传文件被分隔的总块数
        int chunks = chunkUploadInfo.getChunks();
        //当前块，从0开始
        int nowChunk = chunkUploadInfo.getChunk();

        MultipartFile file = chunkUploadInfo.getFile();
        //原始文件名
        String originalFilename = file.getOriginalFilename();

        //第一次上传  创建文件夹   保存元数据
        if (nowChunk == 0) {
            String dir = this.realPath + this.chunkPath + System.currentTimeMillis() + "-" + originalFilename;
            chunkUploadInfo.setDir(dir);
            File chunkPathDir = new File(dir);
            if (!chunkPathDir.exists()) {
                chunkPathDir.mkdirs();
            }
            File saveFilePath = new File(dir + "/" + originalFilename + ".part" + nowChunk);
            if (saveChunkFile(saveFilePath, file)) {
                chunkInfoMapper.insert(chunkUploadInfo);
            }
            //非第一次  更新元数据
        } else if (nowChunk > 0 && nowChunk < chunks) {
            ChunkInfo chunkInfo = chunkInfoMapper.selectByPrimaryKey(chunkUploadInfo.getId());
            File saveFilePath = new File(chunkInfo.getDir() + "/" + originalFilename + ".part" + nowChunk);
            if (saveChunkFile(saveFilePath, file)) {
                chunkInfoMapper.updateByPrimaryKeySelective(chunkUploadInfo);
            }
            //最后一块上传完后合并
            if (nowChunk == chunks - 1) {
                File dir = new File(chunkInfo.getDir());
                File result = mergeFile(dir, System.currentTimeMillis() + "-" + chunkUploadInfo.getName());
                if (result.length() == chunkUploadInfo.getSize()) {
                    //删除分块文件
                    delChunkInfo(chunkInfo);
                    return BaseResult.ok(this.server + this.mergePath + "/" + result.getName());
                }
            }
        }
        chunkUploadInfo.setFile(null);
        return BaseResult.ok(chunkUploadInfo);
    }

    /**
     * 保存块文件
     *
     * @param target
     * @param multipartFile
     * @return
     */
    public boolean saveChunkFile(File target, MultipartFile multipartFile) {
        boolean result;
        try {
            FileUtils.writeByteArrayToFile(target, multipartFile.getBytes());
            result = true;
        } catch (IOException e) {
            target.delete();
            result = false;
        }
        return result;
    }

    /**
     * 合并文件
     *
     * @param dir
     * @param fileName
     * @return
     */
    public File mergeFile(File dir, String fileName) {
        File result = null;
        if (dir.isDirectory()) {
            File[] files = dir.listFiles();
            File mergePath = new File(this.realPath + this.mergePath);
            if (!mergePath.exists()) {
                mergePath.mkdirs();
            }
            File mergeFile = new File(mergePath.getPath() + "/" + fileName);
            try (RandomAccessFile bos = new RandomAccessFile(mergeFile, "rw");) {

                for (File file : files) {
                    //移动偏移量到末尾
                    bos.seek(mergeFile.length());
                    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
                    int len = 0;
                    byte[] b = new byte[1024 * 10];
                    while ((len = bis.read(b)) != -1) {
                        bos.write(b, 0, len);
                    }
                    bis.close();
                }
                result = mergeFile;
            } catch (IOException e) {
                result = null;
                mergeFile.delete();
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 获取分片信息
     *
     * @param id
     * @return
     */
    public BaseResult getChunkInfo(String id) {
        ChunkInfo chunkInfo = chunkInfoMapper.selectByPrimaryKey(id);
        if (chunkInfo == null) {
            chunkInfo = new ChunkInfo();
            chunkInfo.setId(id);
            chunkInfo.setChunk(0);
        }
        return BaseResult.ok(chunkInfo);
    }


    /**
     * 删除分片信息
     *
     * @param chunkInfo
     * @return
     */
    public boolean delChunkInfo(ChunkInfo chunkInfo) {
        //删除数据库
        int i = chunkInfoMapper.deleteByPrimaryKey(chunkInfo.getId());
        //删除分块文件
        File file = new File(chunkInfo.getDir());
        for (File eachFile : file.listFiles()) {
            eachFile.delete();
        }
        return file.delete();
    }


    /**
     * 分片上传到FastDFS
     *
     * @param chunkUploadInfo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public BaseResult chunkUploadToDFS(ChunkUploadInfo chunkUploadInfo) throws IOException {


        Integer chunk = chunkUploadInfo.getChunk();
        Integer chunks = chunkUploadInfo.getChunks();
        String name = chunkUploadInfo.getName();

        MultipartFile file = chunkUploadInfo.getFile();

        ChunkInfo chunkInfo = chunkInfoMapper.selectByPrimaryKey(chunkUploadInfo.getId());

        //第一次上传
        if (chunk == 0) {
            if (chunkInfo == null) {
                chunkInfo = new ChunkInfo();
                StorePath storePath = appendFileStorageClient.uploadAppenderFile("file", file.getInputStream(), file.getSize(), name.substring(name.lastIndexOf(".") + 1));
                BeanUtils.copyProperties(chunkUploadInfo, chunkInfo);
                chunkInfo.setFdfsFullPath(storePath.getFullPath());
                chunkInfo.setFdfsPath(storePath.getPath());
                chunkInfo.setFdfsGroup(storePath.getGroup());
                chunkInfo.setOffset(file.getSize());
                chunkInfoMapper.insertSelective(chunkInfo);
            } else {
                return BaseResult.build(StatusCode.CURRENT_CHUNK_INVALID);
            }
        } else if (chunk > 0 && chunk < chunks) {
            if (chunkInfo == null || !chunkInfo.getChunk().equals(chunk - 1)) {
                return BaseResult.build(StatusCode.CURRENT_CHUNK_INVALID);
            }
            String path = chunkInfo.getFdfsPath();
            String fullPath = chunkInfo.getFdfsFullPath();
            String group = chunkInfo.getFdfsGroup();
            // 修改偏移量
            appendFileStorageClient.modifyFile(group, path, file.getInputStream(), file.getSize(), chunkInfo.getOffset());
            // 追加
            appendFileStorageClient.appendFile(group, path, file.getInputStream(), file.getSize());
            if (chunk == chunks - 1) {
                chunkInfoMapper.deleteByPrimaryKey(chunkUploadInfo.getId());
                return BaseResult.ok(fdfsServer + "/" + fullPath);
            } else {
                chunkInfo.setOffset(chunkInfo.getOffset() + file.getSize());
                chunkInfo.setChunk(chunkUploadInfo.getChunk());
                chunkInfoMapper.updateByPrimaryKeySelective(chunkInfo);
            }
        }
        chunkUploadInfo.setFile(null);
        return BaseResult.ok(chunkUploadInfo);
    }
}
